package com.jeff.utils.security;

import java.io.UnsupportedEncodingException;

/**
 * author：duff
 * version：1.0.0
 * date：2017/8/31
 * desc：
 */
public class RC4Util {
    private static final int BYTE_MASK = 0xFF;

    private static final byte[] RAW_KEY = {99, 7, 66, 74, -81, -36, 45, -18, 106, 25, 126, 6, 71, -67, 47, 108,
            116, 117, 113, 121, 78, 109, -119, 62, 29, 23, 24, 76, 125, -62, -89, 0,
            -72, 82, 102, 50, -51, 100, 81, 65, -105, -26, -65, 88, -74, 89,
            -69, 18, 87, 44, -23, -20, -63, 36, -88, 79, -32, 26, -115, -10, -127, -49,
            124, -122, -6, 122, 91, 12, 70, -29, -31, 14, -2, -12, -123, 84, 27, 97, -87,
            -84, 98, 41, 120, 59, 39, -104, 52, 101, 33, 35, -17, 105, -86, -57, 75, -76,
            -128, 64, 3, -33, 119, 95, 57, -73, 60, -68, 1, 69, 46, 86, 42, -98, -99, -9, 118,
            -95, -16, -41, 54, -124, 17, -107, 10, -121, -97, -106, 55, -14, -96, -116, 110,
            -4, -94, 56, -21, 30, -55, -47, 31, 58, 37, -46, 83, -30, -93, 61, 123, -120, -85,
            -111, -66, -102, -113, -50, -110, -8, -91, 68, 107, 112, -27, -64, 90, -45, 4, -118,
            115, -92, 72, -15, 67, -83, -56, -52, 51, -39, -19, -13, 114, 2, -53, -1, 16, -48, -7,
            -58, -117, -77, 93, 53, -101, -38, -126, -44, 11, -59, 103, 5, -80, 63, -82, -114, -5,
            -71, -28, -78, -42, -103, 19, 48, -43, 49, 34, -25, 22, 92, 80, -22, 32, 127, 104, -125,
            20, 38, 15, 94, -11, -100, 8, -3, -79, 43, -24, 73, 9, -54, -108, -35, 96, -109, 111, -34,
            -40, -60, 77, 40, 28, 13, -70, 21, -75, -90, -37, -112, -61, 85};

    /**
     * 加密
     *
     * @param string 需要加密的字符串
     * @return 加密后的数据
     */
    public static String encrypt(String string) {
        if (string == null) {
            return null;
        }

        try {
            string = asString(rc4Base(string.getBytes("UTF-8")));
        } catch (UnsupportedEncodingException e) {
            string = "";
        }
        return toHexString(string);
    }

    /**
     * 解密
     *
     * @param string 需要解密的字符串
     * @return 解密后的数据，错误返回 null
     */
    public static String decrypt(String string) {
        if (string == null) {
            return null;
        }

        return new String(rc4Base(hexString2Bytes(string)));
    }

    private static String asString(byte[] buf) {
        StringBuffer stringBuffer = new StringBuffer(buf.length);
        for (int i = 0; i < buf.length; i++) {
            stringBuffer.append((char) buf[i]);
        }
        return stringBuffer.toString();
    }

    private static String toHexString(String s) {
        String str = "";
        for (int i = 0; i < s.length(); i++) {
            int ch = (int) s.charAt(i);
            String s4 = Integer.toHexString(ch & BYTE_MASK);
            if (s4.length() == 1) {
                s4 = '0' + s4;
            }
            str = str + s4;
        }
        return str;
    }

    private static byte[] hexString2Bytes(String src) {
        int size = src.length();
        byte[] ret = new byte[size / 2];
        byte[] tmp = src.getBytes();
        for (int i = 0; i < size / 2; i++) {
            ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]);
        }
        return ret;
    }

    private static byte uniteBytes(byte src0, byte src1) {
        char b0 = (char) Byte.decode("0x" + new String(new byte[]{src0}))
                .byteValue();
        final int bitOffset = 4;
        b0 = (char) (b0 << bitOffset);
        char b1 = (char) Byte.decode("0x" + new String(new byte[]{src1}))
                .byteValue();
        byte ret = (byte) (b0 ^ b1);
        return ret;
    }


    private static byte[] rc4Base(byte[] input) {
        int x = 0;
        int y = 0;
        byte[] key = RAW_KEY.clone();

        int xorIndex;
        byte[] result = new byte[input.length];

        for (int i = 0; i < input.length; i++) {
            x = (x + 1) & BYTE_MASK;
            y = ((key[x] & BYTE_MASK) + y) & BYTE_MASK;
            byte tmp = key[x];
            key[x] = key[y];
            key[y] = tmp;
            xorIndex = ((key[x] & BYTE_MASK) + (key[y] & BYTE_MASK)) & BYTE_MASK;
            result[i] = (byte) (input[i] ^ key[xorIndex]);
        }
        return result;
    }
}
